package org.infodavid.common.impl.persistence;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.PersistenceException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.joda.time.DateTime;

import org.infodavid.common.annotations.AImplementation;
import org.infodavid.common.annotations.AImplementation.ERuntime;
import org.infodavid.common.dto.ITemplateCategoryListItem;
import org.infodavid.common.ejb.TemplateCategoryBean;
import org.infodavid.common.model.ITemplateCategory;
import org.infodavid.common.model.criteria.ICategoryCriteria;
import org.infodavid.common.model.impl.HibernateHelper;
import org.infodavid.common.persistence.IQueryParameter;
import org.infodavid.common.persistence.PersistenceSession;
import org.infodavid.common.persistence.TemplateCategoryDataService;
import org.infodavid.common.persistence.impl.DataServiceHelper;
import org.infodavid.common.persistence.impl.PersistenceUtil;
import org.infodavid.common.services.exceptions.ServiceException;

/**
 * The Class TemplateCategoryDataServiceImpl.
 */
@AImplementation(value = TemplateCategoryDataService.class, runtime = ERuntime.DEFAULT)
public final class TemplateCategoryDataServiceImpl extends AbstractTemplateCategoryDataService {

  /** The Constant log. */
  private static final Log LOGGER = LogFactory.getLog(AbstractTemplateCategoryDataService.class);

  /** The helper. */
  private final DataServiceHelper<ITemplateCategoryListItem,ITemplateCategory,Long> helper;

  /**
   * The Constructor.
   */
  private TemplateCategoryDataServiceImpl() {
    super();

    helper = new DataServiceHelper<ITemplateCategoryListItem,ITemplateCategory,Long>(this);
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.persistence.IDataService#getEntityClass()
   */
  public Class<? extends ITemplateCategory> getEntityClass() {
    return TemplateCategoryBean.class;
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.persistence.TemplateCategoryDataService#findByName(org.infodavid.common.
   * persistence.PersistenceSession, java.lang.String)
   */
  @Override
  public ITemplateCategory findByName(final PersistenceSession session, final String value)
      throws PersistenceException, ServiceException {
    return helper.findByName(session, value, null, QUERY_FIND_BY_NAME);
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see
   * org.infodavid.common.persistence.IDataService#find(org.infodavid.common.persistence.PersistenceSession
   * , java.util.List, org.infodavid.common.model.criteria.ISearchCriteria)
   */
  @SuppressWarnings("unchecked")
  @Override
  public long find(final PersistenceSession session,
      final List<ITemplateCategoryListItem> entities, final ICategoryCriteria criteria)
      throws PersistenceException {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Searching by criteria using session = " + session + ", entities = " + entities
          + ", criteria = " + criteria);
    }

    PersistenceUtil.validate(session);
    helper.validate(entities);

    final List<IQueryParameter> params = new ArrayList<IQueryParameter>();
    final HibernateHelper hbnHelper = HibernateHelper.getInstance();
    final Criteria hbnCriteria = hbnHelper.createCriteria(session, TemplateCategoryBean.class);
    final Criteria hbnCountCriteria = hbnHelper.createCriteria(session, TemplateCategoryBean.class);

    if (criteria == null) {
      hbnCriteria.add(Restrictions.eq(ITemplateCategory.ARCHIVED, Boolean.FALSE));
      hbnCountCriteria.add(Restrictions.eq(ITemplateCategory.ARCHIVED, Boolean.FALSE));
    }
    else {
      final Boolean archived = criteria.isArchived();
      final String name = criteria.getNamePattern();

      if (StringUtils.isNotEmpty(name)) {
        hbnCriteria.add(Restrictions.like(ITemplateCategory.NAME, name));
        hbnCountCriteria.add(Restrictions.like(ITemplateCategory.NAME, name));
      }

      if (archived == null) {
        hbnCriteria.add(Restrictions.eq(ITemplateCategory.ARCHIVED, Boolean.FALSE));
        hbnCountCriteria.add(Restrictions.eq(ITemplateCategory.ARCHIVED, Boolean.FALSE));
      }
      else {
        hbnCriteria.add(Restrictions.eq(ITemplateCategory.ARCHIVED, archived));
        hbnCountCriteria.add(Restrictions.eq(ITemplateCategory.ARCHIVED, archived));
      }
    }

    PersistenceUtil.applyCriteria(params, criteria);
    hbnHelper.applyParameters(params, hbnCriteria);

    try {
      hbnCountCriteria.setProjection(Projections.count(ITemplateCategory.KEY));
      session.flush();

      final long count = ((Number)hbnCountCriteria.uniqueResult()).longValue();

      if (count > 0) {
        entities.addAll(hbnCriteria.list());
      }

      return count;
    }
    finally {
      PersistenceUtil.releaseQueryParameters(params);
    }
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see
   * org.infodavid.common.persistence.IDataService#clear(org.infodavid.common.persistence.PersistenceSession
   * , java.util.List)
   */
  public void clear(final PersistenceSession session, final List<IQueryParameter> parameters)
      throws PersistenceException {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Removing entities using session = " + session + ", parameters = " + parameters);
    }

    session.removeEntities(session.findEntities(ITemplateCategory.class, QUERY_FIND, parameters),
        null);
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see
   * org.infodavid.common.persistence.IDataService#count(org.infodavid.common.persistence.PersistenceSession
   * , java.util.List)
   */
  public long count(final PersistenceSession session, final List<IQueryParameter> parameters)
      throws PersistenceException {
    return helper.count(session, parameters, QUERY_FIND_COUNT);
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see
   * org.infodavid.common.persistence.IDataService#find(org.infodavid.common.persistence.PersistenceSession
   * , java.util.List, java.util.List)
   */
  public long find(final PersistenceSession session,
      final List<ITemplateCategoryListItem> entities, final List<IQueryParameter> parameters)
      throws PersistenceException {
    return helper.findListItems(session, entities, parameters, QUERY_FIND, QUERY_FIND_COUNT);
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see
   * org.infodavid.common.persistence.IDataService#findByCreationDate(org.infodavid.common.persistence
   * .PersistenceSession, org.joda.time.DateTime, org.joda.time.DateTime, java.util.List,
   * java.util.List)
   */
  public long findByCreationDate(final PersistenceSession session, final DateTime date1,
      final DateTime date2, final List<ITemplateCategoryListItem> entities,
      final List<IQueryParameter> parameters) throws PersistenceException {
    PersistenceUtil.validate(session);

    final List<IQueryParameter> params = PersistenceUtil.initializeQueryParameters(parameters);

    try {
      PersistenceUtil.applySorting(params, ITemplateCategory.CREATION_DATE);

      return helper.findListItemsByDate(session, date1, date2, entities, params,
          QUERY_FIND_BY_CREATION_DATE, QUERY_FIND_BY_CREATION_DATE_COUNT);
    }
    finally {
      PersistenceUtil.releaseQueryParameters(params);
    }
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see
   * org.infodavid.common.persistence.IDataService#findByModificationDate(org.infodavid.common.persistence
   * .PersistenceSession, org.joda.time.DateTime, org.joda.time.DateTime, java.util.List,
   * java.util.List)
   */
  public long findByModificationDate(final PersistenceSession session, final DateTime date1,
      final DateTime date2, final List<ITemplateCategoryListItem> entities,
      final List<IQueryParameter> parameters) throws PersistenceException {
    PersistenceUtil.validate(session);

    final List<IQueryParameter> params = PersistenceUtil.initializeQueryParameters(parameters);

    try {
      PersistenceUtil.applySorting(params, ITemplateCategory.MODIFICATION_DATE);

      return helper.findListItemsByDate(session, date1, date2, entities, params,
          QUERY_FIND_BY_MODIFICATION_DATE, QUERY_FIND_BY_MODIFICATION_DATE_COUNT);
    }
    finally {
      PersistenceUtil.releaseQueryParameters(params);
    }
  }
}
